/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "the_black_morass.h"
#include "Player.h"
#include "TemporarySummon.h"
#include "SpellInfo.h"
#include "ScriptedCreature.h"


const Position PortalLocation[4]=
{
    {-2041.06f, 7042.08f, 29.99f, 1.30f},
    {-1968.18f, 7042.11f, 21.93f, 2.12f},
    {-1885.82f, 7107.36f, 22.32f, 3.07f},
    {-1928.11f, 7175.95f, 22.11f, 3.44f}
};

class instance_the_black_morass : public InstanceMapScript
{
public:
    instance_the_black_morass() : InstanceMapScript("instance_the_black_morass", 269) { }

    InstanceScript* GetInstanceScript(InstanceMap* map) const
    {
        return new instance_the_black_morass_InstanceMapScript(map);
    }

    struct instance_the_black_morass_InstanceMapScript : public InstanceScript
    {
        instance_the_black_morass_InstanceMapScript(Map* map) : InstanceScript(map) { }

		std::set<uint64> encounterNPCs;
		uint32 encounters[MAX_ENCOUNTER];
        uint64 _medivhGUID;
        uint8  _currentRift;
		uint8  _shieldPercent;

        void Initialize()
        {
			memset(&encounters, 0, sizeof(encounters));
            _medivhGUID = 0;
            _currentRift = 0;
			_shieldPercent = 100;
			encounterNPCs.clear();
        }

		void CleanupInstance()
		{
			Events.Reset();
            _currentRift = 0;
			_shieldPercent = 100;

			instance->LoadGrid(-2023.0f, 7121.0f);
			if (Creature* medivh = instance->GetCreature(_medivhGUID))
			{
				medivh->DespawnOrUnsummon();
				medivh->SetRespawnTime(3);
			}

			std::set<uint64> eCopy = encounterNPCs;
			for (std::set<uint64>::const_iterator itr = eCopy.begin(); itr != eCopy.end(); ++itr)
				if (Creature* creature = instance->GetCreature(*itr))
					creature->DespawnOrUnsummon();
		}

        bool IsEncounterInProgress() const
        {
            return false;
        }

        void OnPlayerEnter(Player* player)
        {
			if (instance->GetPlayersCountExceptGMs() <= 1 && GetData(TYPE_AEONUS) != DONE)
				CleanupInstance();

			player->SendUpdateWorldState(WORLD_STATE_BM, _currentRift > 0 ? 1 : 0);
            player->SendUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
            player->SendUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
        }

        void OnCreatureCreate(Creature* creature)
        {
            switch (creature->GetEntry())
			{
				case NPC_MEDIVH:
					_medivhGUID = creature->GetGUID();
					break;
				case NPC_TIME_RIFT:
				case NPC_CHRONO_LORD_DEJA:
				case NPC_INFINITE_CHRONO_LORD:
				case NPC_TEMPORUS:
				case NPC_INFINITE_TIMEREAVER:
				case NPC_AEONUS:
				case NPC_RIFT_KEEPER_WARLOCK:
				case NPC_RIFT_KEEPER_MAGE:
				case NPC_RIFT_LORD:
				case NPC_RIFT_LORD_2:
				case NPC_INFINITE_ASSASIN:
				case NPC_INFINITE_WHELP:
				case NPC_INFINITE_CRONOMANCER:
				case NPC_INFINITE_EXECUTIONER:
				case NPC_INFINITE_VANQUISHER:
					encounterNPCs.insert(creature->GetGUID());
					break;
			}
        }

		void OnCreatureRemove(Creature* creature)
		{
            switch (creature->GetEntry())
			{
				case NPC_TIME_RIFT:
				case NPC_CHRONO_LORD_DEJA:
				case NPC_INFINITE_CHRONO_LORD:
				case NPC_TEMPORUS:
				case NPC_INFINITE_TIMEREAVER:
				case NPC_AEONUS:
				case NPC_RIFT_KEEPER_WARLOCK:
				case NPC_RIFT_KEEPER_MAGE:
				case NPC_RIFT_LORD:
				case NPC_RIFT_LORD_2:
				case NPC_INFINITE_ASSASIN:
				case NPC_INFINITE_WHELP:
				case NPC_INFINITE_CRONOMANCER:
				case NPC_INFINITE_EXECUTIONER:
				case NPC_INFINITE_VANQUISHER:
					encounterNPCs.erase(creature->GetGUID());
					break;
			}
        }

        void SetData(uint32 type, uint32 data)
        {
            switch (type)
            {
				case TYPE_AEONUS:
				{
					encounters[type] = DONE;
					SaveToDB();

					if (Creature* medivh = instance->GetCreature(_medivhGUID))
						medivh->AI()->DoAction(ACTION_OUTRO);

                    Map::PlayerList const& players = instance->GetPlayers();
                    if (!players.isEmpty())
                        for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
                            if (Player* player = itr->GetSource())
                            {
                                if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
                                    player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);

                                if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
                                    player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
							}
					break;
                }
				case TYPE_CHRONO_LORD_DEJA:
				case TYPE_TEMPORUS:
					encounters[type] = DONE;
					Events.RescheduleEvent(EVENT_NEXT_PORTAL, 60000);
					Events.SetPhase(1);
					SaveToDB();
					break;
				case DATA_RIFT_KILLED:
					if (!Events.IsInPhase(1))
						Events.RescheduleEvent(EVENT_NEXT_PORTAL, 4000);
					break;
				case DATA_MEDIVH:
					DoUpdateWorldState(WORLD_STATE_BM, 1);
					DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
					DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
					Events.RescheduleEvent(EVENT_NEXT_PORTAL, 3000);
					break;
				case DATA_DAMAGE_SHIELD:
					--_shieldPercent;
					DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
					if (!_shieldPercent)
						if (Creature* medivh = instance->GetCreature(_medivhGUID))
							if (medivh->IsAlive())
							{
								Unit::Kill(medivh, medivh);

								// Xinef: delete all spawns
								std::set<uint64> eCopy = encounterNPCs;
								for (std::set<uint64>::iterator itr = eCopy.begin(); itr != eCopy.end(); ++itr)
									if (Creature* creature = instance->GetCreature(*itr))
										creature->DespawnOrUnsummon();
							}
					break;
            }
        }

        uint32 GetData(uint32 type) const
        {
			switch (type)
			{
				case TYPE_CHRONO_LORD_DEJA:
				case TYPE_TEMPORUS:
				case TYPE_AEONUS:
					return encounters[type];
				case DATA_SHIELD_PERCENT:
					return _shieldPercent;
				case DATA_RIFT_NUMBER:
					return _currentRift;
			}
            return 0;
        }

		void SetData64(uint32 type, uint64 data)
		{
			if (type == DATA_SUMMONED_NPC)
				encounterNPCs.insert(data);
			else if (type == DATA_DELETED_NPC)
				encounterNPCs.erase(data);
		}

        uint64 GetData64(uint32 data) const
        {
            if (data == DATA_MEDIVH)
                return _medivhGUID;

            return 0;
        }

        void SummonPortalKeeper()
        {
			Creature* rift = NULL;
			for (std::set<uint64>::const_iterator itr = encounterNPCs.begin(); itr != encounterNPCs.end(); ++itr)
				if (Creature* summon = instance->GetCreature(*itr))
					if (summon->GetEntry() == NPC_TIME_RIFT)
					{
						rift = summon;
						break;
					}

			if (!rift)
				return;

			int32 entry = 0;
			switch (_currentRift)
			{
				case 6: entry = GetData(TYPE_CHRONO_LORD_DEJA) == DONE ? (instance->IsHeroic() ? NPC_INFINITE_CHRONO_LORD : -NPC_CHRONO_LORD_DEJA) : NPC_CHRONO_LORD_DEJA; break;
				case 12: entry = GetData(TYPE_TEMPORUS) == DONE ? (instance->IsHeroic() ? NPC_INFINITE_TIMEREAVER : -NPC_TEMPORUS) : NPC_TEMPORUS; break;
				case 18: entry = NPC_AEONUS; break;
				default: entry = RAND(NPC_RIFT_KEEPER_WARLOCK, NPC_RIFT_KEEPER_MAGE, NPC_RIFT_LORD, NPC_RIFT_LORD_2); break;
			}

            Position pos;
            rift->GetNearPosition(pos, 10.0f, 2*M_PI*rand_norm());

            if (TempSummon* summon = instance->SummonCreature(abs(entry), pos))
			{
				summon->SetTempSummonType(TEMPSUMMON_CORPSE_TIMED_DESPAWN);
				summon->SetTimer(3*MINUTE*IN_MILLISECONDS);

				if (entry < 0)
					summon->SetLootMode(0);

				if (summon->GetEntry() != NPC_AEONUS)
				{
					rift->AI()->SetGUID(summon->GetGUID());
					rift->CastSpell(summon, SPELL_RIFT_CHANNEL, false);
				}
				else
					summon->SetReactState(REACT_DEFENSIVE);
			}
        }

        void Update(uint32 diff)
        {
            Events.Update(diff);
            switch (Events.ExecuteEvent())
            {
				case EVENT_NEXT_PORTAL:
					++_currentRift;
					DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
					Events.ScheduleEvent(EVENT_SUMMON_KEEPER, 6000);
					Events.SetPhase(0);

					if (Creature* medivh = instance->GetCreature(_medivhGUID))
					{
						uint8 position = (_currentRift-1)%4;
						instance->SummonCreature(NPC_TIME_RIFT, PortalLocation[position]);
					}
					break;
				case EVENT_SUMMON_KEEPER:
					SummonPortalKeeper();
					break;
            }
        }

		std::string GetSaveData()
		{
			OUT_SAVE_INST_DATA;

			std::ostringstream saveStream;
			saveStream << "B M " << encounters[0] << ' ' << encounters[1] << ' ' << encounters[2];

			OUT_SAVE_INST_DATA_COMPLETE;
			return saveStream.str();
		}

		void Load(const char* in)
		{
			if (!in)
			{
				OUT_LOAD_INST_DATA_FAIL;
				return;
			}

			OUT_LOAD_INST_DATA(in);

			char dataHead1, dataHead2;

			std::istringstream loadStream(in);
			loadStream >> dataHead1 >> dataHead2;
			if (dataHead1 == 'B' && dataHead2 == 'M')
			{
				for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
					loadStream >> encounters[i];
			}
			else
				OUT_LOAD_INST_DATA_FAIL;

			OUT_LOAD_INST_DATA_COMPLETE;
		}

        protected:
            EventMap Events;
    };

};

void AddSC_instance_the_black_morass()
{
    new instance_the_black_morass();
}
